﻿using System;
using System.Drawing;

namespace CloudDetector.Utils
{
    #region Nested Types
    /// <summary>
    /// Helyvektort ( origóból induló) megvalósító osztály.
    /// </summary>
    public struct LocalVector
    {
        #region Fields

        private double x_coord;
        private double y_coord;

        #endregion

        #region Properties

        /// <summary>
        /// A helyvektorból egy <see cref="Point"/> tipusba átalakító.
        /// </summary>
        public Point Point
        {
            get { return new Point((int)x_coord, (int)y_coord); }
        }

        public double Y
        {
            get { return y_coord; }
            set { y_coord = value; }
        }

        public double X
        {
            get { return x_coord; }
            set { x_coord = value; }
        }

        #endregion

        #region Contructors

        public LocalVector(double d)
        {
            x_coord = y_coord = d;
        }

        public LocalVector(double x, double y)
        {
            x_coord = x;
            y_coord = y;
        }

        #endregion

        #region Operators

        public static LocalVector operator -(LocalVector v1, LocalVector v2)
        {
            return new LocalVector(v1.X - v2.X, v1.Y - v2.Y);
        }

        #endregion

        #region Member Methods

        /// <summary>
        /// A koordináta számok értékét 2 tizedesjegyre vágja vissza.
        /// </summary>
        /// <returns>Kisebb pontosságú helyvektor.</returns>
        public LocalVector Cut()
        {
            return new LocalVector(Math.Round(X, 2), Math.Round(Y, 2));
        }

        /// <summary>
        /// A helyvektor hosszának meghatározása.
        /// </summary>
        /// <returns>A helyvektor hossza.</returns>
        public double Magnitude()
        {
            return Math.Sqrt(this.X * this.X + this.Y * this.Y);
        }

        /// <summary>
        /// Két helyvektor által bezárt szög meghatározása.
        /// </summary>
        /// <param name="v1">Egyik helyvektor.</param>
        /// <param name="v2">Másik helyvektor.</param>
        /// <returns>A két vektor által bezárt szög.</returns>
        public static double Angle(LocalVector v1, LocalVector v2)
        {
            return Math.Acos(LocalVector.DotProduct(v1,v2) / (v1.Magnitude() * v2.Magnitude()));
        }

        /// <summary>
        /// Két helyvektor skaláris szorzását számoló metódus.
        /// </summary>
        /// <param name="v1">Egyik helyvektor.</param>
        /// <param name="v2">Másik helyvektor.</param>
        /// <returns>Skaláris szorzat érték.</returns>
        public static double DotProduct(LocalVector v1, LocalVector v2)
        {
            return v1.X * v2.X + v1.Y * v2.Y;
        }

        #endregion

        /// <summary>
        /// Felüldefiniált szöveggé alakító metódus.
        /// </summary>
        /// <returns>A <see cref="LocalVector"/> tipusú objektumok szöveges formája.</returns>
        public override string ToString()
        {
            return "(" + X.ToString() + ", " + Y.ToString() + ")";
        }

    }

    #endregion

    /// <summary>
    /// Szabadvektorokat (két helyvektor által meghatározott) megvalósító osztály.
    /// </summary>
    public class FreeVector
    {
        #region Fields

        private LocalVector pStartVertex;
        private LocalVector pEndVertex;
        private Boolean pSelected;

        #endregion

        #region Properties

        public Boolean Selected
        {
            get { return pSelected; }
            private set 
            { 
                pSelected ^= value; 
            }
        }

        public LocalVector EndVertex
        {
            get { return pEndVertex; }
            set 
            { 
                pEndVertex = value;
            }
        }

        public LocalVector StartVertex
        {
            get { return pStartVertex; }
            set 
            { 
                pStartVertex = value;
            }
        }

        #endregion

        #region Constructors

        public FreeVector()
        {
            Selected = false;
        }

        public FreeVector(LocalVector end)
        {
            EndVertex = end;
            Selected = false;
        }

        public FreeVector(LocalVector start, LocalVector end)
        {
            StartVertex = start;
            EndVertex = end;
            Selected = false;
        }

        public FreeVector(double startx, double starty, double endx, double endy)
        {
            StartVertex = new LocalVector(startx, starty);
            EndVertex = new LocalVector(endx, endy);
            Selected = false;
        }

        #endregion

        #region Member Methods

        /// <summary>
        /// Szabadvektor kijelölése adott pont alapján.
        /// </summary>
        /// <param name="location">Referencia pont.</param>
        public void Select(Point location)
        { 
            Rectangle boundingRect;
            
            if(Math.Sqrt(Math.Pow(StartVertex.X, 2) + Math.Pow(StartVertex.Y, 2)) 
                <
               Math.Sqrt(Math.Pow(EndVertex.X, 2) + Math.Pow(EndVertex.Y, 2)))
                boundingRect = new Rectangle((int)StartVertex.X, (int)StartVertex.Y, (int)Math.Abs(EndVertex.X - StartVertex.X), (int)Math.Abs(EndVertex.Y - StartVertex.Y));
            else
                boundingRect = new Rectangle((int)EndVertex.X, (int)EndVertex.Y, (int)Math.Abs(EndVertex.X - StartVertex.X), (int)Math.Abs(EndVertex.Y - StartVertex.Y));
            Selected = boundingRect.Contains(location);
        }

        /// <summary>
        /// Szabadvektor rajzolását megvalósító metódus.
        /// </summary>
        /// <param name="gr">Grafikus felület, ahova rajzolunk.</param>
        public void Draw(Graphics gr)
        {
            Pen normPen = new Pen(Color.Orange, 1f);
            Pen selPen = new Pen(Color.Red, 2f);
            
            gr.DrawLine(Selected ? selPen : normPen , StartVertex.Point, EndVertex.Point);
        }

        /// <summary>
        /// Visszaadja egy szabadvektor hosszát.
        /// </summary>
        /// <returns>Szabadvektor hossza.</returns>
        public double Magnitude()
        {
            LocalVector vect2d = EndVertex - StartVertex;
            return Math.Sqrt(vect2d.X * vect2d.X + vect2d.Y * vect2d.Y);
        }

        /// <summary>
        /// Meghatározza a szabadvektor irányvektorát.
        /// </summary>
        /// <returns>Irányvektor.</returns>
        public LocalVector Normalize()
        {
            LocalVector vect2d = EndVertex - StartVertex;
            double inverse = 1 / Magnitude();
            return new LocalVector(vect2d.X * inverse, vect2d.Y * inverse);
        }

        /// <summary>
        /// Felüldefiniált szöveggé alakító metódus.
        /// </summary>
        /// <returns>A <see cref="FreeVector"/> tipusú objektumok szöveges formája.</returns>
        public override string ToString()
        {
            LocalVector vect2d = EndVertex - StartVertex;
            return "vector(" + vect2d.X.ToString() + ", " + vect2d.Y.ToString() + ")";
        }

        #endregion
    }
}
